<?php

namespace thinkct\library;

use GuzzleHttp\Client;
use think\facade\Request;
use think\facade\Cache;
use think\facade\Config;
use think\facade\Db;

class Verify
{
    protected object $request;
    protected string $api_url = 'https://v2.thinkct.net';
    protected string $api_query;
    protected string $api_update;
    protected string $update_file = 'update.zip';
    protected string $update_sql = 'update.sql';
    protected mixed $system_type;
    protected mixed $system_key;
    protected mixed $version;

    protected object $client;
    public function __construct()
    {
        $this->system_type = Config::get('system.type');
        $this->system_key = Config::get('system.key');
        $this->version = Config::get('version.version');
        $this->request = Request::instance();
        $this->api_query = $this->api_url . '/api/v2/query';
        $this->api_update = $this->api_url . '/api/v2/update';
        $this->client = new Client();
    }

    public function query(): array
    {
        try {
            $result = Cache::get('sys_query');

            if (!isset($result['today']) || $result['today'] !== $this->getToday()) {
                $client = new Client();
                $response = $client->post($this->api_query, [
                    'form_params' => [
                        'name' => $this->system_type,
                        'key' => $this->system_key,
                        'host' => $this->request->host(),
                        'version' => $this->version,
                    ],
                    'timeout' => 5,
                    'verify' => false,
                ]);
                $result = json_decode($response->getBody()->getContents(), true);
                $result['code'] !== 1 ? die : null;
                Cache::set('sys_query', [
                    'today' => $this->getToday(),
                    'data' => $result['data'],
                    'msg' => $result['msg']
                ]);
                // 授权状态
                $auth = $result['data']['status'] == 1;
                Cache::set('sys_auth', $auth);
            }
            return $result['data'];
        } catch (\Exception $exception) {
            // 如果出现异常，直接返回
            Cache::set('sys_query', [
                'today' => $this->getToday(),
                'data' => []
            ]);
            return ['code' => 0, 'msg' => $exception->getMessage()];
        }
    }

    public function update(int $renew = 0): array
    {
        try {
            $response = $this->client->post($this->api_update, [
                'form_params' => [
                    'name' => $this->system_type,
                    'key' => $this->system_key,
                    'host' => $this->request->host(),
                    'version' => $this->version,
                    'renew' => $renew,
                ],
                'verify' => false,
            ]);

            $result = json_decode($response->getBody()->getContents(), true);

            $update_version = $result['data']['version'] ?? '';
            $update_file = $result['data']['file'] ?? '';
            // 如果是立即更新
            if ($renew === 1 && !empty($update_file)) {
                // 下载文件
                $download_file = $this->download($update_file);
                if ($download_file) {
                    // 导入sql语句
                    $sqlFile = root_path() . $this->update_sql;
                    if (is_file($sqlFile)) {
                        $sql = file_get_contents($sqlFile);
                        $sqlList = $this->parse_sql($sql, 0, ['thinkct_' => config('database.connections.mysql.prefix')]);
                        if ($sqlList) {
                            $sqlList = array_filter($sqlList);
                            $update_sql_data = Cache::get('update_sql') ?? [];
                            foreach ($sqlList as $value) {
                                try {
                                    $update_sql = Cache::get('update_sql') ?? [];
                                    $update_sql_data[] = $value;
                                    if (in_array($value, $update_sql)) {
                                        continue;
                                    }
                                    Db::execute($value);
                                    Cache::set('update_sql1', $update_sql_data);
                                } catch(\Exception $e) {
                                    trace('sql失败语句:' . $value, 'update_error_sql');
                                }
                            }
                        }
                        unlink($sqlFile);
                    }
                }
                $result['msg'] = '更新版本v' . $update_version . '成功';
            }

            return $result;
        } catch (\Exception $exception) {
            return ['code' => 0, 'msg' => $exception->getMessage()];
        }
    }

    public function update_log(): array
    {
        $response = $this->client->post($this->api_url . '/api/v2/update_log', [
            'form_params' => [
                'name' => $this->system_type,
            ],
            'verify' => false,
        ]);

        $result = json_decode($response->getBody()->getContents(), true);
        return [
            'code' => 1,
            'msg' => $result['msg'] ?? '',
            'data' => $result['data'] ?? [],
        ];
    }

    // 获取今天日期
    private function getToday(): string
    {
        return date('Y-m-d', time());
    }

    // 下载文件
    private function download($file): bool
    {
        if (!empty($file)) {
            // 下载文件
            $response = $this->client->get($file, [
                'verify' => false,
            ]);
            // 保存到根目录
            $file = root_path() . $this->update_file;
            file_put_contents($file, $response->getBody()->getContents());
            // 解压文件
            $zip = new \ZipArchive();
            $zip->open($file);
            $zip->extractTo(root_path());
            $zip->close();
            // 删除文件
            unlink($file);
            return true;
        }
        return false;
    }

    /**
     * 解析sql语句
     * @param  string $content sql内容
     * @param  int $limit  如果为1，则只返回一条sql语句，默认返回所有
     * @param  array $prefix 替换表前缀
     * @return array|string 除去注释之后的sql语句数组或一条语句
     */
    private function parse_sql($sql = '', $limit = 0, $prefix = []): array|string
    {
        // 被替换的前缀
        $from = '';
        // 要替换的前缀
        $to = '';

        // 替换表前缀
        if (!empty($prefix)) {
            $to   = current($prefix);
            $from = current(array_flip($prefix));
        }

        if ($sql != '') {
            // 纯sql内容
            $pure_sql = [];

            // 多行注释标记
            $comment = false;

            // 按行分割，兼容多个平台
            $sql = str_replace(["\r\n", "\r"], "\n", $sql);
            if(empty($sql)) $sql = '';
            $sql = explode("\n", trim($sql));

            // 循环处理每一行
            foreach ($sql as $key => $line) {
                // 跳过空行
                if ($line == '') {
                    continue;
                }

                // 跳过以#或者--开头的单行注释
                if (preg_match("/^(#|--)/", $line)) {
                    continue;
                }

                // 跳过以/**/包裹起来的单行注释
                if (preg_match("/^\/\*(.*?)\*\//", $line)) {
                    continue;
                }

                // 多行注释开始
                if (substr($line, 0, 2) == '/*') {
                    $comment = true;
                    continue;
                }

                // 多行注释结束
                if (substr($line, -2) == '*/') {
                    $comment = false;
                    continue;
                }

                // 多行注释没有结束，继续跳过
                if ($comment) {
                    continue;
                }

                // 替换表前缀
                if ($from != '') {
                    $line = str_replace('`'.$from, '`'.$to, $line);
                }
                if ($line == 'BEGIN;' || $line =='COMMIT;') {
                    continue;
                }
                // sql语句
                array_push($pure_sql, $line);
            }

            // 只返回一条语句
            if ($limit == 1) {
                return implode("", $pure_sql);
            }

            // 以数组形式返回sql语句
            $pure_sql = implode(";\n", $pure_sql);
            if(empty($pure_sql)) $pure_sql = '';
            $pure_sql = explode(";\n", $pure_sql);
            return $pure_sql;
        } else {
            return $limit == 1 ? '' : [];
        }
    }
}